namespace Com.FirstSolver.Splash;

public abstract class ECCurve
{
	protected ECFieldElement a;

	protected ECFieldElement b;

	public abstract int mFieldSize { get; }

	public abstract ECPoint Infinity { get; }

	public ECFieldElement mA => a;

	public ECFieldElement mB => b;

	public abstract ECFieldElement FromBigInteger(BigInteger x);

	public abstract ECPoint CreatePoint(BigInteger x, BigInteger y, bool withCompression);

	protected internal abstract ECPoint DecompressPoint(int yTilde, BigInteger X1);

	public virtual ECPoint DecodePoint(byte[] encoded, int offset, out int length)
	{
		int FieldSizeInBytes = mFieldSize + 7 >> 3;
		byte PC = encoded[offset];
		switch (PC)
		{
		case 0:
			length = 1;
			return Infinity;
		case 2:
		case 3:
		{
			length = FieldSizeInBytes + 1;
			if (offset + length > encoded.Length)
			{
				return null;
			}
			BigInteger X = new BigInteger(1, encoded, offset + 1, FieldSizeInBytes);
			return DecompressPoint(PC & 1, X);
		}
		case 4:
		case 6:
		case 7:
		{
			length = (FieldSizeInBytes << 1) + 1;
			if (offset + length > encoded.Length)
			{
				return null;
			}
			BigInteger X2 = new BigInteger(1, encoded, offset + 1, FieldSizeInBytes);
			BigInteger Y = new BigInteger(1, encoded, offset + 1 + FieldSizeInBytes, FieldSizeInBytes);
			return new FpPoint(this, FromBigInteger(X2), FromBigInteger(Y), withCompression: false);
		}
		default:
			length = 0;
			return null;
		}
	}

	public override bool Equals(object obj)
	{
		if (obj == this)
		{
			return true;
		}
		if (!(obj is ECCurve other))
		{
			return false;
		}
		return Equals(other);
	}

	protected bool Equals(ECCurve other)
	{
		return a.Equals(other.a) && b.Equals(other.b);
	}

	public override int GetHashCode()
	{
		return a.GetHashCode() ^ b.GetHashCode();
	}
}
